using System;
using System.Collections.Generic;
using GTA;
using GTA.UI;
using GTA.Native;

namespace CustomPeds;

public class Behavior : Script
{
    private List<Ped> enemiesList = new List<Ped>();
    private DateTime lastEnemyCheck = DateTime.MinValue;
    private DateTime lastFollowCheck = DateTime.MinValue;
    private readonly TimeSpan enemyCheckInterval = TimeSpan.FromSeconds(1);
    private readonly TimeSpan followCheckInterval = TimeSpan.FromSeconds(1);
    private Ped[] cachedNearbyPeds = new Ped[0];
    private DateTime lastPedCache = DateTime.MinValue;
    private readonly TimeSpan pedCacheInterval = TimeSpan.FromSeconds(2);
    private DateTime lastVoice = DateTime.MinValue;
    private readonly TimeSpan voiceCooldown = TimeSpan.FromSeconds(6);
    private readonly float protectRadius = 30f;
    private readonly float guardRepositionDistance = 8f;
    private DateTime lastHazardCheck = DateTime.MinValue;
    private readonly TimeSpan hazardCheckInterval = TimeSpan.FromMilliseconds(600);

    public Behavior()
    {
        Tick += OnTick;
        Interval = 1000;
    }

    private void OnTick(object sender, EventArgs e)
    {
        try
        {
            if (Main.P2.Character != null && Main.P2.Character.IsAlive)
            {
                if (!Main.IsPedDriverOfCurrentVehicle(Main.P2.Character) && Main.P2.AutoAttack)
                {
                    AttackNearbyEnemies();
                }
                if (Main.P2.Follow)
                {
                    CheckFollow();
                }
                CheckIsInvincible();
                CheckAvoidHazards();
            }
            if (Main.P2.IsTaskDriveToWaypoint)
            {
                EnforceGirlfriendDriverState();
            }
            CheckCanWearHelmet();
            CheckDrivingStyle();
            CheckChangeSeat();
        }
        catch (Exception ex)
        {
            LogHelper.WriteException(ex, "Behavior.OnTick - 主要行为检查");
            Notification.PostTicker($"行为脚本错误: {ex.Message}", true, false);
        }
    }

    private void EnforceGirlfriendDriverState()
    {
        try
        {
            Vehicle vehicle = null;
            if (Main.P1.Character != null && Main.P1.Character.IsInVehicle())
            {
                vehicle = Main.P1.Character.CurrentVehicle;
            }
            else if (Main.P2.Character != null && Main.P2.Character.IsInVehicle())
            {
                vehicle = Main.P2.Character.CurrentVehicle;
            }
            if (vehicle == null || !vehicle.Exists())
            {
                return;
            }
            if (Main.P2.IsTaskDriveToWaypoint)
            {
                return;
            }
            if ((Main.P1.Character != null && Main.IsPedDriverOfCurrentVehicle(Main.P1.Character)) ||
                (Main.P2.Character != null && !Main.IsPedDriverOfCurrentVehicle(Main.P2.Character)))
            {
                Main.EnsureDriverAndPassenger(vehicle, Main.P2.Character, Main.P1.Character);
            }
        }
        catch (Exception ex)
        {
            LogHelper.WriteException(ex, "EnforceGirlfriendDriverState - 强制女友为司机");
        }
    }

    private void CheckCanWearHelmet()
    {
        try
        {
            if (Main.P1.Character != null && Main.P1.Character.IsAlive && !Main.P1.CanWearHelmet)
            {
                Main.P1.Character.RemoveHelmet(instantly: true);
            }
            if (Main.P2.Character != null && Main.P2.Character.IsAlive && !Main.P2.CanWearHelmet)
            {
                Main.P2.Character.RemoveHelmet(instantly: true);
            }
        }
        catch (Exception ex)
        {
            LogHelper.WriteException(ex, "CheckCanWearHelmet - 头盔检查");
        }
    }

    private void CheckIsInvincible()
    {
        try
        {
            Main.P2.Character.IsInvincible = Main.P2.Invincible;
        }
        catch (Exception ex)
        {
            LogHelper.WriteException(ex, "CheckIsInvincible - 无敌状态检查");
        }
    }

    private void CheckDrivingStyle()
    {
        try
        {
            if (Main.P1.Character != null && Main.P1.Character.IsAlive)
            {
                Main.P1.Character.VehicleDrivingFlags = Main.P1.drivingFlags;
            }
            if (Main.P2.Character != null && Main.P2.Character.IsAlive)
            {
                Main.P2.Character.VehicleDrivingFlags = Main.P2.drivingFlags;
            }
        }
        catch (Exception ex)
        {
            LogHelper.WriteException(ex, "CheckDrivingStyle - 驾驶风格检查");
        }
    }

    private void CheckChangeSeat()
    {
        try
        {
            if (Main.P1.Character != null && Main.P1.Character.IsAlive && Main.P1.Character.IsInVehicle() && !Main.P1.IsTaskDriveToWaypoint)
            {
                var p1Vehicle = Main.P1.Character.CurrentVehicle;
                var p1TargetSeat = (VehicleSeat)Main.P1.SeatIndex;
                if (p1Vehicle != null && p1Vehicle.Exists() && !Main.IsPedInSeat(p1Vehicle, Main.P1.Character, p1TargetSeat))
                {
                    var p1SeatFree = Main.IsSeatFree(p1Vehicle, p1TargetSeat);
                    if (p1SeatFree)
                    {
                        Main.P1.Character.Task.EnterVehicle(p1Vehicle, p1TargetSeat, 3000, 2f, EnterVehicleFlags.WarpIn);
                    }
                }
            }

            if (Main.P2.Character == null || !Main.P2.Character.IsAlive || !Main.P2.Character.IsInVehicle() ||
                Main.P2.IsTaskDriveToWaypoint) return;
            if (Main.P1.Character == null || !Main.P1.Character.IsAlive || !Main.P1.Character.IsInVehicle()) return;
            var targetVehicle = Main.P1.Character.CurrentVehicle;
            var p2TargetSeat = (VehicleSeat)Main.P2.SeatIndex;
            if (targetVehicle == null || !targetVehicle.Exists() ||
                Main.IsPedInSeat(targetVehicle, Main.P2.Character, p2TargetSeat)) return;
            var p2SeatFree = Main.IsSeatFree(targetVehicle, p2TargetSeat);
            if (p2SeatFree)
            {
                Main.P2.Character.Task.EnterVehicle(targetVehicle, p2TargetSeat, 3000, 2f, EnterVehicleFlags.WarpIn);
            }
        }
        catch (Exception ex)
        {
            LogHelper.WriteException(ex, "CheckChangeSeat - 换座检查");
        }
    }

    private void CheckFollow()
    {
        if (DateTime.Now.Subtract(lastFollowCheck) < followCheckInterval)
        {
            return;
        }
        lastFollowCheck = DateTime.Now;
        try
        {
            if (Main.P1.Character == null || Main.P2.Character == null ||
                !(Main.P1.Character.Position.DistanceTo(Main.P2.Character.Position) > 20f)) return;
            Main.P2.Character.Position = Main.P1.Character.GetOffsetPosition(new GTA.Math.Vector3(0f, -3f, -1f));
            if (!Main.P1.Character.PedGroup.Contains(Main.P2.Character))
            {
                Main.P1.Character.PedGroup.Add(Main.P2.Character, leader: false);
            }
        }
        catch (Exception ex)
        {
            LogHelper.WriteException(ex, "CheckFollow - 跟随检查");
        }
    }

    private void AttackNearbyEnemies()
    {
        if (DateTime.Now.Subtract(lastEnemyCheck) < enemyCheckInterval)
        {
            return;
        }
        lastEnemyCheck = DateTime.Now;
        try
        {
            if (DateTime.Now.Subtract(lastPedCache) >= pedCacheInterval)
            {
                var anchor = (Main.P2.Character != null && Main.P2.Character.Exists()) ? Main.P2.Character.Position : Game.Player.Character.Position;
                cachedNearbyPeds = World.GetNearbyPeds(anchor, 150f);
                lastPedCache = DateTime.Now;
            }
            enemiesList.RemoveAll(ped => ped == null || !ped.Exists() || ped.IsDead);
            if (Main.P1.Character == null || Main.P2.Character == null)
            {
                return;
            }

            // 计算玩家血量比例，用于触发撤离掩护/护主优先
            float playerHealthRatio = 1f;
            try
            {
                int maxHp = Math.Max(1, Main.P1.Character.MaxHealth);
                playerHealthRatio = (float)Main.P1.Character.Health / maxHp;
            }
            catch { }

            var threatsToP1 = new List<Ped>();
            var otherThreats = new List<Ped>();
            foreach (var ped in cachedNearbyPeds)
            {
                if (ped == null || !ped.Exists())
                {
                    continue;
                }
                if (!ped.IsAlive || ped.Handle == Game.Player.Character.Handle || ped.Handle == Main.P2.Character.Handle)
                {
                    continue;
                }
                bool hostile = ped.IsInCombatAgainst(Main.P1.Character) || ped.IsInCombatAgainst(Main.P2.Character);
                if (!hostile)
                {
                    continue;
                }
                if (ped.IsInCombatAgainst(Main.P1.Character))
                {
                    threatsToP1.Add(ped);
                }
                else
                {
                    otherThreats.Add(ped);
                }
            }

            threatsToP1.Sort((a, b) => a.Position.DistanceTo(Main.P1.Character.Position).CompareTo(b.Position.DistanceTo(Main.P1.Character.Position)));
            otherThreats.Sort((a, b) => a.Position.DistanceTo(Main.P2.Character.Position).CompareTo(b.Position.DistanceTo(Main.P2.Character.Position)));

            bool playerLowHealth = playerHealthRatio <= 0.35f;
            if (playerLowHealth)
            {
                // 低血量时贴身护卫并优先攻击威胁玩家的目标
                try
                {
                    if (Main.P2.Character.Position.DistanceTo(Main.P1.Character.Position) > guardRepositionDistance)
                    {
                        Main.P2.Character.Position = Main.P1.Character.GetOffsetPosition(new GTA.Math.Vector3(0f, -2f, -1f));
                        Speak("COVER_ME");
                    }
                }
                catch { }

                Ped target = (threatsToP1.Count > 0) ? threatsToP1[0] : (otherThreats.Count > 0 ? otherThreats[0] : null);
                if (target != null && !enemiesList.Exists(p => p.Handle == target.Handle))
                {
                    Main.P2.Character.Task.Combat(target);
                    enemiesList.Add(target);
                }
                return;
            }

            // 正常护主：优先清理威胁玩家的敌人，保持靠近玩家的护卫半径
            if (Main.P2.Character.Position.DistanceTo(Main.P1.Character.Position) > protectRadius)
            {
                try
                {
                    Main.P2.Character.Position = Main.P1.Character.GetOffsetPosition(new GTA.Math.Vector3(0f, -3f, -1f));
                }
                catch { }
            }

            if (threatsToP1.Count > 0)
            {
                var target = threatsToP1[0];
                if (!enemiesList.Exists(p => p.Handle == target.Handle))
                {
                    Main.P2.Character.Task.Combat(target);
                    enemiesList.Add(target);
                    Speak("GENERIC_SHOUTS");
                }
                return;
            }

            // 其次处理其它威胁
            foreach (var ped in otherThreats)
            {
                if (enemiesList.Exists(p => p.Handle == ped.Handle))
                {
                    continue;
                }
                Main.P2.Character.Task.Combat(ped);
                enemiesList.Add(ped);
            }
        }
        catch (Exception ex)
        {
            LogHelper.WriteException(ex, "AttackNearbyEnemies - 攻击附近敌人");
        }
    }

    private void CheckAvoidHazards()
    {
        if (DateTime.Now.Subtract(lastHazardCheck) < hazardCheckInterval)
        {
            return;
        }
        lastHazardCheck = DateTime.Now;
        try
        {
            if (Main.P2 == null || Main.P2.Character == null || !Main.P2.Character.IsAlive || !Main.P2.AvoidHazards)
            {
                return;
            }
            var p1 = Main.P1?.Character;
            var p2 = Main.P2?.Character;
            if (p1 == null || p2 == null)
            {
                return;
            }
            float radius = GetHazardRadius(Main.P2.HazardSensitivityIndex);
            bool hazardNear = IsHazardAround(p1.Position, radius) || IsHazardAround(p2.Position, radius) || p1.IsOnFire || p2.IsOnFire;
            if (!hazardNear)
            {
                return;
            }

            float back = GetRetreatDistance(Main.P2.HazardSensitivityIndex);
            var target = p1.GetOffsetPosition(new GTA.Math.Vector3(0f, -back, -1f));
            if (p2.Position.DistanceTo(target) > 2f)
            {
                p2.Position = target;
                Speak("GENERIC_SHOCKED_HIGH");
            }
        }
        catch (Exception ex)
        {
            LogHelper.WriteException(ex, "CheckAvoidHazards - 爆炸/火焰规避");
        }
    }

    private float GetHazardRadius(int sensitivityIndex)
    {
        switch (sensitivityIndex)
        {
            case 0: return 12f; // 低
            case 2: return 26f; // 高
            default: return 18f; // 中
        }
    }

    private float GetRetreatDistance(int sensitivityIndex)
    {
        switch (sensitivityIndex)
        {
            case 0: return 8f;
            case 2: return 16f;
            default: return 12f;
        }
    }

    private bool IsHazardAround(GTA.Math.Vector3 pos, float radius)
    {
        try
        {
            int fires = Function.Call<int>(Hash.GET_NUMBER_OF_FIRES_IN_RANGE, pos.X, pos.Y, pos.Z, radius);
            if (fires > 0)
            {
                return true;
            }
            // 检测多种常见爆炸类型
            for (int t = 0; t <= 9; t++)
            {
                if (Function.Call<bool>(Hash.IS_EXPLOSION_IN_SPHERE, t, pos.X, pos.Y, pos.Z, radius))
                {
                    return true;
                }
            }
        }
        catch { }
        return false;
    }

    private void Speak(string clip)
    {
        try
        {
            if (DateTime.Now.Subtract(lastVoice) < voiceCooldown)
            {
                return;
            }
            if (Main.P2.Character != null && Main.P2.Character.Exists())
            {
                Main.P2.Character.PlayAmbientSpeech1(clip, immediately: true);
                lastVoice = DateTime.Now;
            }
        }
        catch { }
    }
}


